home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / utils / file / managers / mc-3.2 / mc-3 / mc-3.2.1 / src / view.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-05-17  |  50.8 KB  |  2,144 lines

  1. /* {{{ Copyright notice */
  2.  
  3. /* View file module for the Midnight Commander
  4.    Copyright (C) 1994, 1995 The Free Software Foundation
  5.    Written by: 1994, 1995 Miguel de Icaza
  6.                1994, 1995 Janne Kukonlehto
  7.                1995 Jakub Jelinek
  8.    
  9.    This program is free software; you can redistribute it and/or modify
  10.    it under the terms of the GNU General Public License as published by
  11.    the Free Software Foundation; either version 2 of the License, or
  12.    (at your option) any later version.
  13.    
  14.    This program is distributed in the hope that it will be useful,
  15.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  16.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17.    GNU General Public License for more details.
  18.  
  19.    You should have received a copy of the GNU General Public License
  20.    along with this program; if not, write to the Free Software
  21.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  22.  
  23. /* }}} */
  24. /* {{{ Declarations */
  25. #include <config.h>
  26. #include <stdio.h>
  27. #include <sys/types.h>
  28. #ifdef HAVE_UNISTD_H
  29. #   include <unistd.h>
  30. #endif
  31. #include <string.h>
  32. #include "tty.h"
  33. #include <sys/stat.h>
  34. #ifdef HAVE_MMAP
  35. #   include <sys/mman.h>
  36. #endif
  37. #include <fcntl.h>
  38. #include <unistd.h>
  39. #include <ctype.h>    /* For toupper() */
  40. #include <stdlib.h>    /* atoi() */
  41. #include <malloc.h>
  42. #include <errno.h>
  43. #include <limits.h>
  44. #include <sys/param.h>
  45.  
  46. #include "mem.h"
  47. #include "mad.h"
  48. #include "util.h"
  49. #include "dlg.h"        /* Needed by widget.h */
  50. #include "widget.h"        /* Needed for buttonbar_new */
  51. #include "color.h"
  52. #include "dialog.h"
  53. #include "file.h"
  54. #include "mouse.h"
  55. #include "global.h"
  56. #include "help.h"
  57. #include "key.h"        /* For mi_getch() */
  58. #include "layout.h"
  59. #if defined(HAVE_RX_H) && defined(HAVE_REGCOMP)
  60. #include <rx.h>
  61. #else
  62. #include "regex.h"
  63. #endif
  64. #include "fs.h"
  65. #include "../vfs/vfs.h"
  66. #include "dir.h"
  67. #include "panel.h" /* Needed for current_panel and other_panel */
  68. #include "win.h"
  69. #include "main.h"        /* For the externs */
  70. #define WANT_WIDGETS
  71. #include "view.h"
  72.  
  73. #ifndef MAP_FILE
  74. #define MAP_FILE 0
  75. #endif
  76.  
  77. /* Block size for reading files in parts */
  78. #define READ_BLOCK 8192
  79. #define VIEW_PAGE_SIZE 8192
  80.  
  81. #ifdef IS_AIX
  82. #   define IFAIX(x) case (x):
  83. #else
  84. #   define IFAIX(x)
  85. #endif
  86.  
  87. /* Maxlimit for skipping updates */
  88. int max_dirt_limit = 
  89. #ifdef _OS_NT
  90. 0;
  91. #else
  92. 10;
  93. #endif
  94.  
  95. /* Our callback */
  96. static int view_callback (Dlg_head *h, WView *view, int msg, int par);
  97.  
  98. /* Wrap mode */
  99. int wrap_mode = 1;
  100.  
  101. /* Scrolling is done in pages or line increments */
  102. int mouse_move_pages_viewer = 1;
  103.  
  104. /* Used to compute the bottom first variable */
  105. int have_fast_cpu = 0;
  106.  
  107. int default_hex_mode = 0;
  108. int default_magic_flag = 1;
  109. int default_nroff_flag = 0;
  110. int altered_hex_mode = 0;
  111. int altered_magic_flag = 0;
  112. int altered_nroff_flag = 0;
  113. /* }}} */
  114.  
  115. /* "$Id: view.c,v 1.19 1995/02/21 19:07:38 miguel Exp $" */
  116.  
  117. static char hex_char[] = "0123456789ABCDEF";
  118.  
  119. /* }}} */
  120. /* {{{ Clean-up functions */
  121.  
  122. void free_file (WView *view)
  123. {
  124.     int i;
  125.     
  126. #ifdef HAVE_MMAP
  127.  
  128.     if (view->mmaping){
  129.     mc_munmap (view->data, view->s.st_size);
  130.     mc_close (view->file);
  131.     } else 
  132. #endif /* HAVE_MMAP */
  133.     {
  134.     if (view->reading_pipe){
  135.         /* Check error messages */
  136.         if (!view->have_frame)
  137.         check_error_pipe ();
  138.         
  139.         /* Close pipe */
  140.         pclose (view->stdfile);
  141.         
  142.         /* Ignore errors because we don't want to hear about broken pipe */
  143.         close_error_pipe (-1, NULL);
  144.     } else {
  145.         if (view->file != -1)
  146.         mc_close (view->file);
  147.     }
  148.     }
  149.     /* Block_ptr may be zero if the file was a file with 0 bytes */
  150.     if (view->growing_buffer && view->block_ptr){
  151.     for (i = 0; i < view->blocks; i++){
  152.         free (view->block_ptr [i].data);
  153.     }
  154.     free (view->block_ptr);
  155.     }
  156. }
  157.  
  158. /* Both views */
  159. void view_done (WView *view)
  160. {
  161.     if (view->view_active){
  162.     free_file (view);
  163.     free (view->filename);
  164.     if (view->command)
  165.         free (view->command);
  166.     }
  167.     view->view_active = 0;
  168.     default_hex_mode = view->hex_mode;
  169.     default_nroff_flag = view->viewer_nroff_flag;
  170.     default_magic_flag = view->viewer_magic_flag;
  171. }
  172.  
  173. static void view_hook (void *);
  174.  
  175. void view_destroy (WView *view)
  176. {
  177.     view_done (view);
  178.     if (view->have_frame)
  179.     delete_hook (&select_file_hook, view_hook);
  180. #ifdef HAVE_TK
  181.     free (view->cache);
  182.     free (view->color_cache);
  183. #endif
  184. }
  185.  
  186. static int get_byte (WView *view, int byte_index)
  187. {
  188.     int page   = byte_index / VIEW_PAGE_SIZE + 1;
  189.     int offset = byte_index % VIEW_PAGE_SIZE;
  190.     int i, n;
  191.     block_ptr_t *tmp;
  192.     
  193.     if (view->growing_buffer){
  194.     if (page > view->blocks){
  195.         tmp = xmalloc (sizeof (block_ptr_t) * page, "get_byte");
  196.         if (view->block_ptr){
  197.         bcopy (view->block_ptr, tmp, sizeof (block_ptr_t) *
  198.                view->blocks);
  199.         free (view->block_ptr);
  200.         } 
  201.         view->block_ptr = tmp;
  202.         for (i = view->blocks; i < page; i++){
  203.         char *p = malloc (VIEW_PAGE_SIZE);
  204.         view->block_ptr [i].data = p;
  205.         if (!p)
  206.             return '\n';
  207.         if (view->stdfile != (FILE *)-1)
  208.             n = fread (p, 1, VIEW_PAGE_SIZE, view->stdfile);
  209.         else
  210.             n = mc_read (view->file, p, VIEW_PAGE_SIZE);
  211.         if (n != -1)
  212.             view->bytes_read += n;
  213.         if (view->s.st_size < view->bytes_read){
  214.             view->bottom_first = -1; /* Invalidate cache */
  215.             view->s.st_size = view->bytes_read;
  216.             view->last_byte = view->bytes_read;
  217.             if (view->reading_pipe)
  218.             view->last_byte = view->first + view->bytes_read;
  219.         }
  220.         /* To force loading the next page */
  221.         if (n == VIEW_PAGE_SIZE && view->reading_pipe){
  222.             view->last_byte++;
  223.         }
  224.         }
  225.         view->blocks = page;
  226.     }
  227.     if (byte_index > view->bytes_read){
  228.         return -1;
  229.     } else
  230.         return view->block_ptr [page-1].data [offset];
  231.     } else {
  232.         if (byte_index >= view->last_byte)
  233.             return -1;
  234.         else
  235.         return view->data [byte_index];
  236.     }
  237. }
  238.  
  239. static char *set_view_init_error (WView *view, char *msg)
  240. {
  241.     view->growing_buffer = 0;
  242.     view->reading_pipe   = 0;
  243.     view->first = 0;
  244.     if (msg){
  245.     view->bytes_read = strlen (msg);
  246.     return strdup (msg);
  247.     }
  248.     return 0;
  249. }
  250.  
  251. /* return values: 0 for success, else points to error message */
  252. static char *init_growing_view (WView *view, char *name, char *filename) 
  253. {
  254.     view->growing_buffer = 1;
  255.  
  256.     if (name){
  257.     view->reading_pipe = 1;
  258.     view->s.st_size = 0;
  259.  
  260.     open_error_pipe ();
  261.     if ((view->stdfile = popen (name, "r")) == NULL){
  262.         close_error_pipe (view->have_frame?-1:1, view->data);
  263.         return set_view_init_error (view, " Can't spawn child program ");
  264.     }
  265.  
  266. #ifndef HAVE_XVIEW
  267.     /* First, check if filter produced any output */
  268.     get_byte (view, 0);
  269.     if (view->bytes_read <= 0){
  270.         pclose (view->stdfile);
  271.         close_error_pipe (view->have_frame?-1:1, view->data);
  272.         return set_view_init_error (view, " Empty output from child filter ");
  273.     }
  274. #endif
  275.     } else {
  276.         view->stdfile = (FILE *)-1;
  277.     if ((view->file = mc_open (filename, O_RDONLY)) == -1)
  278.         return set_view_init_error (view, " Could not open file ");
  279.     }
  280.     return 0;
  281. }
  282.  
  283. /* Load filename into core */
  284. /* returns:
  285.    -1 on failure.
  286.    if (have_frame), we return success, but data points to a
  287.    error message instead of the file buffer (quick_view feature).
  288. */
  289. static char *load_view_file (WView *view, char *filename)
  290. {
  291.     char *cmd;
  292.  
  293. #ifndef HAVE_MMAP
  294.     int count;
  295. #endif
  296.  
  297.     if ((view->file = mc_open (filename, O_RDONLY)) < 0){
  298.     set_view_init_error (view, 0);
  299.     return (copy_strings (" Can't open file \"",
  300.                   filename, "\"\n ",
  301.                   unix_error_string (errno), " ", 0));
  302.  
  303.     }
  304.     if (mc_fstat (view->file, &view->s) < 0){
  305.     set_view_init_error (view, 0);
  306.     mc_close (view->file);
  307.     return copy_strings (" Can't stat file \n ",
  308.                  unix_error_string (errno), " ", 0);
  309.     }
  310.     if (S_ISDIR (view->s.st_mode) || S_ISSOCK (view->s.st_mode)
  311.     || S_ISFIFO (view->s.st_mode)){
  312.     mc_close (view->file);
  313.     return set_view_init_error (view, " Can't view: not a regular file ");
  314.     }
  315.  
  316. #ifdef JUST_FOR_CULTURAL_INFORMATION
  317.     /* #ifed out because now we have growing buffers :-) */
  318.     if (view->s.st_size < 1) {
  319.     mc_close (view->file);
  320.     return set_view_init_error (view, " Can't view empty file ");
  321.     }
  322. #endif
  323.     if (view->s.st_size == 0){
  324.     /* Must be one of those nice files that grow (/proc) */
  325.     mc_close (view->file);
  326.     return init_growing_view (view, 0, filename);
  327.     }
  328.     
  329.     /* First, try to open a compressed file */
  330.     if (!view->viewer_magic_flag && (is_gunzipable (view->file)) != 0){
  331.         mc_close (view->file);
  332.     cmd = copy_strings ("gzip -dc ", "'", filename, "'", 0);
  333.     return init_growing_view (view, cmd, filename);
  334.     }
  335.  
  336.     /* Otherwise, the file wasn't compressed */
  337. #ifdef HAVE_MMAP
  338.     view->data = mc_mmap (0, view->s.st_size, PROT_READ, MAP_FILE | MAP_SHARED,
  339.                   view->file, 0);
  340.     if ((caddr_t) view->data == (caddr_t) -1){
  341.     mc_close (view->file);
  342. /*    set_view_init_error (view, 0);
  343.     return copy_strings (" Can't mmap file \n ",
  344.                  unix_error_string (errno), " ", 0);*/
  345.     return init_growing_view (view, 0, filename);
  346.                  
  347.     }
  348.     view->first = 0;
  349.     view->bytes_read = view->s.st_size;
  350.     view->mmaping = 1;
  351.     return 0;
  352. #else
  353.     mc_close (view->file);
  354.     return init_growing_view (view, 0, filename);
  355. #endif
  356. }
  357.  
  358. /* Return zero on success, -1 on failure */
  359. int do_view_init (WView *view, char *_command, char *_file)
  360. {
  361.     char *error = 0;
  362.     int i;
  363.  
  364.     if (view->view_active)
  365.     view_done (view);
  366.  
  367.     /* Set up the state */
  368.     view->block_ptr = 0;
  369.     view->data = NULL;
  370.     view->growing_buffer = 0;
  371.     view->reading_pipe = 0;
  372.     view->mmaping = 0;
  373.     view->blocks = 0;
  374.     view->block_ptr = 0;
  375.     view->first = view->bytes_read = 0;
  376.  
  377.     /* Clear the markers */
  378.     view->marker = 0;
  379.     for (i = 0; i < 10; i++)
  380.     view->marks [i] = 0;
  381.     
  382.  
  383.     if (!view->have_frame){
  384.     /* hex_mode = 0; */
  385.     /* wrap_mode = 1; */
  386.     view->start_col = 0;
  387.     }
  388.     if (_command && view->viewer_magic_flag)
  389.     error = init_growing_view (view, _command, _file);
  390.     else
  391.     error = load_view_file (view, _file);
  392.  
  393.     if (error){
  394.     if (!view->have_frame){
  395.         message (1, " Error ", error);
  396.         free (error);
  397.         return -1;
  398.     }
  399.     }
  400.  
  401.     view->view_active = 1;
  402.     view->filename = strdup (_file);
  403.     if (_command)
  404.         view->command = strdup (_command);
  405.     else
  406.         view->command = 0;
  407.     view->search_start = view->start_display = view->first;
  408.     view->found_len = 0;
  409.     view->start_col = 0;
  410.     view->last_search = 0;            /* Start a new search */
  411.  
  412.     /* Special case: The data points to the error message */
  413.     if (error){
  414.     view->data = error;
  415.     view->s.st_size = view->bytes_read = strlen (view->data);
  416.     }
  417.     view->last_byte = view->first + view->s.st_size;
  418.  
  419.     return 0;
  420. }
  421.  
  422.  
  423. /* Both views */
  424. /* Return zero on success, -1 on failure */
  425. int view_init (WView *view, char *_command, char *_file)
  426. {
  427.     int cols;
  428.  
  429.     if (view->have_frame)
  430.     cols = view->widget.cols - 2;
  431.     else
  432.     cols = view->widget.cols;
  433.     
  434.     view->bottom_first = -1;
  435.     view->bytes_per_line = 2 * (cols - 7) / 9;
  436.     view->bytes_per_line &= 0xfffc;
  437.     view->dirty = max_dirt_limit + 1;    /* To force refresh */
  438.     if (!view->view_active || strcmp (_file, view->filename))
  439.     return do_view_init (view, _command, _file);
  440.     else
  441.     return 0;
  442. }
  443.  
  444. /* }}} */
  445.  
  446. /* {{{ X Window code */
  447. #ifdef HAVE_TK
  448. #include "tkmain.h"
  449.  
  450. /* Accepts the dim command with the width and the height in characters */
  451. static int
  452. tk_viewer_callback (ClientData cd, Tcl_Interp *interp, int ac, char *av[])
  453. {
  454.     WView *view = (WView *) cd;
  455.  
  456.     if (av [1][0] != 'd')
  457.     return TCL_OK;
  458.     view->widget.cols  = atoi (av [2]);
  459.     view->widget.lines = atoi (av [3]);
  460.     return TCL_OK;
  461. }
  462.  
  463. void x_create_viewer (WView *view)
  464. {
  465.     char *cmd;
  466.     char *parent;
  467.     
  468.     /* First, check if our parent is ".", if this is the case, then
  469.      * create a stand alone viewer, otherwise, we create a paneled
  470.      * version of the viewer
  471.      */
  472.     
  473.     if (view->have_frame){
  474.     parent = view->widget.wcontainer;
  475.     } else {
  476.     parent = view->widget.parent->wdata;
  477.     }
  478.     cmd = tk_new_command (parent, view, tk_viewer_callback, 'v');
  479.     
  480.     tk_evalf ("newview %d %s %s %s", view->have_frame,
  481.           view->have_frame ? (char *) view->widget.wcontainer : "{}",
  482.           cmd+1, cmd);
  483. }
  484. #else
  485. #   define x_create_viewer(x)
  486. #endif
  487. /* }}} */
  488.  
  489. /* {{{ Screen update functions */
  490.  
  491. #ifdef HAVE_TK
  492. static void view_status (WView *view)
  493. {
  494.     char *window = wtk_win (view->widget);
  495.     
  496.     if (!view->status_shown){
  497.     view->status_shown = 0;
  498.  
  499.     tk_evalf ("view_update_info %s {%s} {%d} {%s} {%s}",
  500.           window, name_trunc (view->filename ? view->filename:
  501.                       view->command ? view->command:"", 20),
  502.           -view->start_col, size_trunc (view->s.st_size),
  503.           view->growing_buffer ? "[grow]":"[]");
  504.     }
  505. }
  506.  
  507. static void view_percent (WView *view, int p, int w)
  508. {
  509.     fprintf (stderr, "Missing tk view_percent\n");
  510. }
  511. #else
  512. static void view_percent (WView *view, int p, int w)
  513. {
  514.     int percent;
  515.     
  516.     percent = view->s.st_size == 0 ? 100 :
  517.     (view->last_byte == view->last ? 100 :
  518.      (p)*100 / view->s.st_size);
  519.     
  520.     widget_move (view, view->have_frame, w - 5);
  521.     printw ("%3d%% ", percent);
  522. }
  523.  
  524. static void view_status (WView *view)
  525. {
  526.     int w = view->widget.cols - (view->have_frame * 2);
  527.     int i;
  528.  
  529.     attrset (SELECTED_COLOR);
  530.     widget_move (view, view->have_frame, view->have_frame);
  531.     hline (' ', w);
  532.     if (w > 6){
  533.         i = w > 24 ? 18 : w - 6;
  534.         printw ("File: %s", name_trunc (view->filename ? view->filename:
  535.                     view->command ? view->command:"", i));
  536.         if (w > 30){
  537.             widget_move (view, view->have_frame, 26);
  538.             printw ("Col %d", -view->start_col);
  539.         }
  540.         if (w > 60){
  541.         widget_move (view, view->have_frame, 42);
  542.         printw ("%s bytes", size_trunc (view->s.st_size));
  543.         }
  544.     if (w > 70){
  545.         printw (" ");
  546.         if (view->growing_buffer)
  547.         addstr ("  [grow]");
  548.     }
  549.         if (w - i > 4)
  550.         view_percent (view, view->start_display-view->first, w);
  551.     }
  552.     attrset (SELECTED_COLOR);
  553. }
  554. #endif
  555.  
  556. #ifdef HAVE_TK
  557.  
  558. #define DEF_COLOR       0
  559. #define BOLD_COLOR      1
  560. #define UNDERLINE_COLOR 2
  561. #define MARK_COLOR      3
  562.  
  563. /* The major part of the Tk code deals with caching a line (the
  564.  * current one) of text to avoid expensive calls to the Tk text widget
  565.  * callback.
  566.  *
  567.  * We cache all this information on view->cache and the colors on
  568.  * view->color_cache.
  569.  *
  570.  * FIXME: the cache does not know about the contents of the physical
  571.  * text widget (depends on your concept of physical), so if we happen
  572.  * to hit the case where row is decremented in the void display () routine,
  573.  * we will end up with a clean line.
  574.  */
  575.  
  576. static int current_color;
  577.  
  578. static void set_color (int font)
  579. {
  580.     current_color = font;
  581. }
  582.  
  583. static inline void display_clean(WView *view, int h, int w)
  584. {
  585.     char *win = wtk_win (view->widget);
  586.  
  587.     tk_evalf ("cleanview %s.v.view", win);
  588. }
  589.  
  590. static void add_character (WView *view, int c)
  591. {
  592.     view->cache [view->dest] = c;
  593.     view->color_cache [view->dest] = current_color;
  594. }
  595.  
  596. static inline void add_string (WView *view, char *s)
  597. {
  598.     while (*s)
  599.     add_character (view, *s++);
  600. }
  601.  
  602. static char *get_tk_tag_name (int color)
  603. {
  604.     /* Those names are the names of the tags in the Tk source */
  605.     static char *color_tag_names [] = {
  606.     "normal", "bold", "underline", "mark"
  607.     };
  608.  
  609.     return color_tag_names [color];
  610. }
  611.  
  612. /*
  613.  * Tk: Flushes the contents of view->cache to the Tk text widget
  614.  *
  615.  * We get the command information and call the command directly
  616.  * to avoid escaping the view->cache contents.
  617.  */
  618. static void flush_line (WView *view)
  619. {
  620.     char *win = wtk_win (view->widget);
  621.     int  row = view->current_line;
  622.     char *text_name;
  623.     Tcl_CmdInfo info;
  624.     int  i, prev_color;
  625.     char str_row [30];
  626.     char *av [5];
  627.     int  len = strlen (view->cache);
  628.  
  629.     /* Fetch the address and clientData for the view */
  630.     text_name = copy_strings (win, ".v.view", 0);
  631.     Tcl_GetCommandInfo (interp, text_name, &info);
  632.  
  633.     /* Setup arguments to the command:
  634.      * $win.v.view insert @$row,0 $view->cache
  635.      */
  636.     sprintf (str_row, "%d.0", row);
  637.     i = 0;
  638.     av [i++] = text_name;
  639.     av [i++] = "insert";
  640.     av [i++] = str_row;
  641.     av [i++] = view->cache;
  642.  
  643.     /* Call the callback :-) */
  644.     (*info.proc) (info.clientData, interp, i, av);
  645.     bzero (view->cache, view->cache_len);
  646.  
  647.     /* Colorize the line */
  648.     for (prev_color = 0, i = 0; i < len; i++){
  649.     int new_color_start;
  650.     char *color_name;
  651.         
  652.     if (view->color_cache [i] == prev_color)
  653.         continue;
  654.  
  655.     new_color_start = i;
  656.     
  657.     prev_color = view->color_cache [i];
  658.     
  659.     for (;i < len && view->color_cache [i] == prev_color; i++)
  660.         ;
  661.     
  662.     color_name = get_tk_tag_name (prev_color);
  663.     tk_evalf ("%s tag add %s %d.%d %d.%d",
  664.           text_name, color_name,
  665.           row, new_color_start-1,
  666.           row, i);
  667.     }
  668.     
  669.     bzero (view->color_cache, view->cache_len);
  670.     view->last_col = 0;
  671.     free (text_name);
  672. }
  673.  
  674. /* Tk: Mantains the line cache */
  675. void view_gotoyx (WView *view, int row, int col)
  676. {
  677.     if (row != view->current_line){
  678.     fprintf (stderr, "%d\n", row);
  679.     flush_line (view);
  680.     }
  681.     view->current_line = row;
  682.  
  683.     /* In case the user has resized the viewer */
  684.     if (col > view->cache_len){
  685.     char *new;
  686.  
  687.     new = xmalloc (col + 1, "cache");
  688.     strcpy (new, view->cache);
  689.     free (view->cache);
  690.     view->cache = new;
  691.  
  692.     new = xmalloc (col + 1, "cache");
  693.     strcpy (new, view->color_cache);
  694.     free (view->color_cache);
  695.     view->color_cache = new;
  696.     
  697.     view->cache_len = col;
  698.     }
  699.  
  700.     view->dest = col;
  701.     for (; view->last_col+1 < col; view->last_col++){
  702.     view->cache [view->last_col] = ' ';
  703.     view->color_cache [view->last_col] = current_color;
  704.     }
  705.     view->last_col = col;
  706. }
  707.  
  708. #else
  709.  
  710. #define BOLD_COLOR        MARKED_COLOR
  711. #define UNDERLINE_COLOR   VIEW_UNDERLINED_COLOR
  712. #define MARK_COLOR        SELECTED_COLOR
  713. #define DEF_COLOR         NORMAL_COLOR
  714.  
  715. #define set_color(font) attrset (font)
  716.  
  717.  
  718. static inline void display_clean (WView *view, int height, int width)
  719. {
  720.     /* FIXME: Should I use widget_erase only and repaint the box? */
  721.     if (view->have_frame){
  722.     int i;
  723.     
  724.     draw_double_box (view->widget.parent, view->widget.y, view->widget.x,
  725.                  view->widget.lines, view->widget.cols);
  726.     for (i = 1; i < height; i++){
  727.         widget_move (view, i, 1);
  728.         printw ("%*s", width-1, "");
  729.     }
  730.     } else
  731.     widget_erase ((Widget *) view);
  732. }
  733.  
  734. #define add_character(view,c) addch (c)
  735. #define add_string(view,s) addstr (s)
  736. #define view_gotoyx(v,r,c) widget_move (v,r,c)
  737.  
  738. #endif
  739.  
  740. /* Shows the file pointed to by *start_display on view_win */
  741. static long display (WView *view)
  742. {
  743. #ifdef HAVE_X
  744. #   define  frame_shift 0
  745. #else
  746.     const int frame_shift = view->have_frame;
  747. #endif
  748.     int col = 0 + frame_shift;
  749.     int row = 1 + frame_shift;
  750.     int height, width;
  751.     long from;
  752.     int c;
  753.     int boldflag = 0;
  754.  
  755.     height = view->widget.lines - frame_shift;
  756.     width = view->widget.cols - frame_shift;
  757.     from = view->start_display;
  758.     set_color (DEF_COLOR);
  759.  
  760.     display_clean (view, height, width);
  761.     
  762.     if (view->hex_mode){
  763.         char hex_buff[10];   /* A temporary buffer for sprintf and mvwaddstr */
  764.         int bytes;         /* Number of bytes already printed on the line */
  765.     /* Start of text column */
  766.         int text_start = width - view->bytes_per_line - 1 + frame_shift;
  767.     
  768.         for (;row < height && from < view->last_byte; row++){
  769.             /* Print the hex offset */
  770.             sprintf (hex_buff, "%05X", (int) (from - view->first));
  771.         widget_move (view, row, frame_shift);
  772.             add_string (view, hex_buff);
  773.         
  774.             /* Hex dump starts from column seven */
  775.             col = 7;
  776.         
  777.             /* Each hex number is two digits */
  778.             hex_buff[2] = 0;
  779.             for (bytes = 0; bytes < view->bytes_per_line
  780.          && from < view->last_byte; bytes++, from++){
  781.                 c = (unsigned char) get_byte (view, from);
  782.         
  783.             if (view->found_len && from >= view->search_start
  784.             && from < view->search_start + view->found_len){
  785.                 boldflag = 1;
  786.             set_color (BOLD_COLOR);
  787.             }
  788.         
  789.                 /* Print a hex number (sprintf is too slow) */
  790.                 hex_buff [0] = hex_char [(c >> 4)];
  791.                 hex_buff [1] = hex_char [c & 15];
  792.         view_gotoyx (view, row, col);
  793.                 add_string (view, hex_buff);
  794.                 col += 3;
  795.                 if ((bytes & 3) == 3 && bytes + 1 < view->bytes_per_line){
  796.                 
  797.                     if (boldflag && from == view->search_start + view->found_len - 1)
  798.                         set_color (DEF_COLOR);
  799.             
  800.                     /* Hex numbers are printed in the groups of four */
  801.                     /* Groups are separated by a vline */
  802.             
  803.                     add_character (view, ' ');
  804.                     one_vline ();
  805.             view_gotoyx (view, row, col + 1);
  806.                     col += 2;
  807.                     
  808.                     if (boldflag && from==view->search_start+view->found_len-1)
  809.                         set_color (BOLD_COLOR);
  810.             
  811.                 }
  812.                 if (boldflag && from < view->search_start + view->found_len - 1 
  813.                     && bytes != view->bytes_per_line - 1)
  814.                     add_character (view, ' ');
  815.         
  816.                 /* Print the corresponding ascii character */
  817.         view_gotoyx (view, row, text_start + bytes);
  818.         
  819.                 if (!is_printable (c))
  820.                     c = '.';
  821.         add_character (view, c);
  822.         
  823.                 if (boldflag){
  824.                     boldflag = 0;
  825.                     set_color (DEF_COLOR);
  826.                 }
  827.             }
  828.         }
  829.     } else {
  830.         if (view->growing_buffer && from == view->last_byte)
  831.             get_byte (view, from);
  832.         for (; row < height && from < view->last_byte; from++){
  833.         c = get_byte (view, from);
  834.             if ((c == '\n') || (col == width && wrap_mode)){
  835.                    col = frame_shift;
  836.                    row++;
  837.         if (c == '\n' || row >= height)
  838.             continue;
  839.                }
  840.         if (c == '\r')
  841.         continue;
  842.                if (c == '\t'){
  843.                    col = ((col - frame_shift)/8)*8 + 8 + frame_shift;
  844.                    continue;
  845.                }
  846.         if (view->viewer_nroff_flag && c == '\b'){
  847.             if (from + 1 < view->last_byte
  848.             && is_printable (get_byte (view, from + 1)) &&
  849.                 from > view->first
  850.             && is_printable (get_byte (view, from - 1)))
  851.         {
  852.             if (col <= frame_shift){
  853.                 /* So it has to be wrap_mode - do not need to check for it */
  854.                 if (row == 1 + frame_shift){
  855.                     from++;
  856.                     continue; /* There had to be a bold character on the rightmost position
  857.                              of the previous undisplayed line */
  858.                 }
  859.                 row--;
  860.                 col = width;
  861.             }
  862.             col--;
  863.             boldflag = 1;
  864.             if (get_byte (view, from - 1) == '_' && get_byte (view, from + 1) != '_')
  865.                 set_color (UNDERLINE_COLOR);
  866.             else
  867.                 set_color (BOLD_COLOR);
  868.             continue;
  869.         }
  870.         }
  871.         if (view->found_len && from >= view->search_start
  872.         && from < view->search_start + view->found_len){
  873.             boldflag = 1;
  874.         set_color (MARK_COLOR);
  875.         }
  876.                if (col >= frame_shift-view->start_col
  877.         && col < width-view->start_col)
  878.         {
  879.         view_gotoyx (view, row, col+view->start_col);
  880.                if (!is_printable (c))
  881.             c = '.';
  882.  
  883.         add_character (view, c);
  884.                } 
  885.         col++;
  886.         if (boldflag){
  887.         boldflag = 0;
  888.         set_color (DEF_COLOR);
  889.         }
  890.         }
  891. #ifdef HAVE_TK
  892.     /* Tk: This flushes the last line */
  893.     view_gotoyx (view, view->current_line+1, 0);
  894. #endif
  895.     
  896.         if (view->growing_buffer && from == view->last_byte)
  897.             get_byte (view, from);
  898.     }
  899.     view->last = from;
  900.     return from;
  901. }
  902.  
  903. void view_update (WView *view)
  904. {
  905.     static int dirt_limit = 1;
  906.  
  907.     if (view->dirty > dirt_limit){
  908.     /* Too many updates skipped -> force a update */
  909.     display (view);
  910.     view_status (view);
  911.     view->dirty = 0;
  912.     /* Raise the update skipping limit */
  913.     dirt_limit++;
  914.     if (dirt_limit > max_dirt_limit)
  915.         dirt_limit = max_dirt_limit;
  916.     }
  917.     if (view->dirty){
  918.     if (is_idle ()){
  919.         /* We have time to update the screen properly */
  920.         display (view);
  921.         view_status (view);
  922.         view->dirty = 0;
  923.         if (dirt_limit > 1)
  924.         dirt_limit--;
  925.     } else {
  926.         /* We are busy -> skipping full update,
  927.            only the status line is updated */
  928.         view_status (view);
  929.     }
  930.     /* Here we had a refresh, if fast scrolling does not work
  931.        restore the refresh, althought this should not happend */
  932.     }
  933. }
  934.  
  935. static inline void my_define (Dlg_head *h, int idx, char *text,
  936.              void (*fn)(WView *), WView *view)
  937. {
  938.     define_label_data (h, (Widget *) view, idx, text, (buttonbarfn) fn, view);
  939. }
  940.  
  941. /* }}} */
  942. /* {{{ Movement functions */
  943. /* If the last parameter is nonzero, it means we want get the count of lines
  944.    from current up to the the upto position inclusive */
  945. static long move_forward2 (WView *view, long current, int lines, long upto)
  946. {
  947.     long p, q;
  948.     int  line;
  949.     int  col = 0;
  950.     
  951.     if (!upto && view->last == view->last_byte)
  952.         return current;
  953.  
  954.     if (view->hex_mode){
  955.         return (p = current + lines * view->bytes_per_line) >= view->last_byte ? current : p;
  956.     } else {
  957.         if (upto){
  958.             lines = -1;
  959.             q = upto;
  960.         } else
  961.             q = view->last_byte;
  962.         for (line = col = 0, p = current; p < q; p++){
  963.         int c;
  964.         
  965.         if (lines != -1 && line >= lines)
  966.             return p;
  967.  
  968.         c = get_byte (view, p);
  969.         
  970.         if (wrap_mode){
  971.             if (c == '\r')
  972.                 continue; /* This characters is never displayed */
  973.             else if (c == '\t')
  974.                 col = ((col - view->have_frame)/8)*8 +8+ view->have_frame;
  975.         else
  976.             col++;
  977.             if (view->viewer_nroff_flag && c == '\b'){
  978.                 if (p + 1 < view->last_byte
  979.             && is_printable (get_byte (view, p + 1))
  980.                     && p > view->first
  981.             && is_printable (get_byte (view, p - 1)))
  982.                 col -= 2;
  983.             } else if (col == vwidth){
  984.             /* FIXME: the c in is_printable was a p, that is a bug,
  985.                I suspect I got that fix from Jakub, same applies
  986.                for d. */
  987.             int d = get_byte (view, p+2);
  988.             
  989.             if (p + 2 >= view->last_byte || !is_printable (c) || 
  990.                 !view->viewer_nroff_flag || get_byte (view, p + 1) != '\b' || 
  991.                 !is_printable (d)){
  992.                 col = 0;
  993.             
  994.                 if (c == '\n' || get_byte (view, p+1) != '\n')
  995.                     line++;
  996.             }
  997.         } else if (c == '\n'){
  998.                 line++;
  999.             col = 0;
  1000.             }
  1001.         } else if (c == '\n')
  1002.             line++;
  1003.         }
  1004.         if (upto)
  1005.             return line;
  1006.     }
  1007.     return current;
  1008. }
  1009.  
  1010. /* returns the new current pointer */
  1011. /* Cause even the forward routine became very complex, we in the wrap_mode
  1012.    just find the nearest '\n', use move_forward2(p, 0, q) to get the count
  1013.    of lines up to there and then use move_forward2(p, something, 0), which we
  1014.    return */
  1015. static long move_backward2 (WView *view, long current, int lines)
  1016. {
  1017.     long p, q;
  1018.     int line;
  1019.  
  1020.     if (current == view->first)
  1021.     return current;
  1022.     
  1023.     if (view->hex_mode){
  1024.     p = current - lines * view->bytes_per_line;
  1025.  
  1026.         return (p < view->first) ? view->first : p;
  1027.     } else {
  1028.         if (current == view->last_byte
  1029.         && get_byte (view, current - 1) != '\n')
  1030.         /* There is one virtual '\n' at the end,
  1031.            so that the last line is shown */
  1032.           line = 1;
  1033.       else
  1034.           line = 0;
  1035.         for (q = p = current - 1; p > view->first; p--)
  1036.         if (get_byte (view, p) == '\n')
  1037.             if (!wrap_mode){
  1038.                 if (line == lines)
  1039.                     return p + 1;
  1040.                 line++;
  1041.             } else {
  1042.                 line += move_forward2 (view, p + 1, 0, q);
  1043.                 if (line >= lines){
  1044.                     if (line == lines)
  1045.                         return p + 1;
  1046.                     else
  1047.                         return move_forward2 (view, p + 1, line - lines, 0);
  1048.                 }
  1049.                 q = p + 1;
  1050.             }
  1051.     }
  1052.     return p;
  1053. }
  1054.  
  1055. static void move_backward (WView *view, int i)
  1056. {
  1057.     view->search_start = view->start_display =
  1058.     move_backward2 (view, view->start_display, i);
  1059.     view->found_len = 0;
  1060.     view->dirty++;
  1061. }
  1062.  
  1063. static long get_bottom_first (WView *view, int do_not_cache, int really)
  1064. {
  1065.     int bottom_first;
  1066.  
  1067.     if (!have_fast_cpu && !really)
  1068.     return INT_MAX;
  1069.     
  1070.     if (!do_not_cache && view->bottom_first != -1)
  1071.         return view->bottom_first;
  1072.  
  1073.     /* Force loading */
  1074.     if (view->growing_buffer){
  1075.     int old_last_byte;
  1076.  
  1077.     old_last_byte = -1;
  1078.     while (old_last_byte != view->last_byte){
  1079.         old_last_byte = view->last_byte;
  1080.         get_byte (view, view->last_byte+VIEW_PAGE_SIZE);
  1081.     }
  1082.     }
  1083.  
  1084.     bottom_first = move_backward2 (view, view->last_byte, vheight - 1);
  1085.     
  1086.     if (view->hex_mode)
  1087.         bottom_first = (bottom_first + view->bytes_per_line - 1)
  1088.         / view->bytes_per_line * view->bytes_per_line;
  1089.     view->bottom_first = bottom_first;
  1090.     
  1091.     return view->bottom_first;
  1092. }
  1093.  
  1094. static void move_forward (WView *view, int i)
  1095. {
  1096.     view->start_display = move_forward2 (view, view->start_display, i, 0);
  1097.     if (!view->reading_pipe && view->start_display > get_bottom_first (view, 0, 0))
  1098.         view->start_display = view->bottom_first;
  1099.     view->search_start = view->start_display;
  1100.     view->found_len = 0;
  1101.     view->dirty++;
  1102. }
  1103.  
  1104.  
  1105. static void move_to_top (WView *view)
  1106. {
  1107.     view->search_start = view->start_display = view->first;
  1108.     view->found_len = 0;
  1109.     view->dirty++;
  1110. }
  1111.  
  1112. static void move_to_bottom (WView *view)
  1113. {
  1114.     view->search_start = view->start_display = get_bottom_first (view, 0, 1);
  1115.     view->found_len = 0;
  1116.     view->dirty++;
  1117. }
  1118.  
  1119. /* Scroll left/right the view panel functions */
  1120. static void move_right (WView *view)
  1121. {
  1122.     if (wrap_mode)
  1123.     return;
  1124.     
  1125.     if (--view->start_col > 0)
  1126.     view->start_col = 0;
  1127.     view->dirty++;
  1128. }
  1129.  
  1130. static void move_left (WView *view)
  1131. {
  1132.     if (wrap_mode)
  1133.     return;
  1134.     
  1135.     if (++view->start_col > 0)
  1136.     view->start_col = 0;
  1137.     view->dirty++;
  1138. }
  1139.  
  1140. /* }}} */
  1141. /* {{{ Search routines */
  1142.  
  1143. /* Case insensitive search of text in data */
  1144. static int icase_search_p (WView *view, char *text, char *data, int nothing)
  1145. {
  1146.     int p = 0;
  1147.     char *q;
  1148.  
  1149.     p = (q = icase_search (text, data)) != 0; 
  1150.     if (p){
  1151.     view->found_len = strlen (text);
  1152.     view->search_start = q - data - view->found_len;
  1153.     }
  1154.     return p;
  1155. }
  1156.  
  1157. static char *grow_string_buffer (char *text, int *size)
  1158. {
  1159.     char *new;
  1160.     int  old_size = *size;
  1161.  
  1162.     /* The grow steps */
  1163.     *size += 160;
  1164.     new = xmalloc (*size, "grow_string_buffer");
  1165.     strncpy (new, text, old_size);
  1166.     if (text)
  1167.     free (text);
  1168.     return new;
  1169. }
  1170.  
  1171. static char *get_line_at (WView *view, long *p)
  1172. {
  1173.     char *buffer = 0;
  1174.     int  buffer_size, usable_size;
  1175.     int  ch;
  1176.     int  direction;
  1177.     long pos = *p;
  1178.     long i;
  1179.  
  1180.     direction = view->direction;
  1181.     buffer_size = usable_size = 0;
  1182.     
  1183.     i = ch = 0;
  1184.     for (;pos >= 0 && (ch = get_byte (view, pos))!= -1; pos += direction, i++){
  1185.  
  1186.     /* skip over all the possible zeros in the file */
  1187.     if (ch == 0 && i == 0){
  1188.         while (pos >= 0 && ((ch = get_byte (view, pos)) != -1) && ch == 0)
  1189.         pos+= direction;
  1190.         if (ch == -1)
  1191.         break;
  1192.     }
  1193.     if (i == usable_size){
  1194.         buffer = grow_string_buffer (buffer, &buffer_size);
  1195.         usable_size = buffer_size - 2;
  1196.         buffer [0] = ' '; /* This makes possible strcpy of buffer */
  1197.     }
  1198.     buffer [i+1] = ch;
  1199.     if (ch == '\n' || !ch || ch == -1){
  1200.         pos += direction; i++;
  1201.         break;
  1202.     }
  1203.     }
  1204.     if (buffer){
  1205.     i--;
  1206.     buffer [0] = ' ';
  1207.     buffer [i+1] = 0;
  1208.     
  1209.     /* If we are searching backwards, reverse the string */
  1210.     if (view->direction < 0)
  1211.         reverse_string (buffer);
  1212.     }
  1213.  
  1214.     *p = pos;
  1215.     return buffer;
  1216. }
  1217.  
  1218. /** Search status optmizations **/
  1219.  
  1220. /* The number of bytes between percent increments */
  1221. int  update_steps;
  1222.  
  1223. /* Last point where we updated the status */
  1224. long update_activate;
  1225.  
  1226. static void search_update_steps (WView *view)
  1227. {
  1228.     if (view->s.st_size)
  1229.     update_steps = 40000;
  1230.     else
  1231.     update_steps = view->last_byte / 100;
  1232.  
  1233.     /* Do not update the percent display but every 20 ks */
  1234.     if (update_steps < 20000)
  1235.     update_steps = 20000;
  1236. }
  1237.  
  1238. static void search (WView *view, char *text,
  1239.             int (*search)(WView *, char *, char *, int))
  1240. {
  1241.     int w = view->widget.cols - (view->have_frame * 2);
  1242.     char *s = NULL;        /*  The line we read from the view buffer */
  1243.     long p, beginning;
  1244.     int  ch;
  1245.     int isatbeg; /* Nonzero means we start search at beginning of some line */
  1246.     int found_len, search_start;
  1247.     Dlg_head *d = 0;
  1248.     int search_status;
  1249.  
  1250.     /* Used to keep track of where the line starts, when looking forward */
  1251.     /* is the index before transfering the line; the reverse case uses   */
  1252.     /* the position returned after the line has been read */
  1253.     long forward_line_start;
  1254.     long reverse_line_start;
  1255.     long t;
  1256.     /* Clear interrupt status */
  1257.     got_interrupt ();
  1258.     
  1259.     if (verbose){
  1260.     d = message (D_INSERT, " Search ", "Searching %s", text);
  1261.     refresh ();
  1262.     }
  1263.     ch = 0;
  1264.     if (view->direction == 1){
  1265.     p = view->found_len ? view->search_start + 1 : view->search_start;
  1266.     } else {
  1267.     p = (view->found_len ? view->search_start : view->last) - 1;
  1268.     }
  1269.     beginning = p;
  1270.  
  1271.     isatbeg = view->found_len == 0;
  1272.     found_len = view->found_len;
  1273.     search_start = view->search_start;
  1274.  
  1275.     /* Compute the percent steps */
  1276.     search_update_steps (view);
  1277.     update_activate = 0;
  1278.  
  1279.     for (; ; isatbeg = 1, free (s)){
  1280. #ifdef HAVE_TK
  1281.     static int count;
  1282.  
  1283.     if ((count++ % 32) == 0)
  1284.         tk_dispatch_all ();
  1285.     if (!d->running)
  1286.         break;
  1287. #endif
  1288.     if (p >= update_activate){
  1289.         update_activate += update_steps;
  1290.         if (verbose){
  1291.         view_percent (view, p, w);
  1292.         refresh ();
  1293.         }
  1294.         if (got_interrupt ())
  1295.         break;
  1296.     }
  1297.     forward_line_start = p;
  1298.     disable_interrupt_key ();
  1299.     s = get_line_at (view, &p);
  1300.     reverse_line_start = p;
  1301.     enable_interrupt_key ();
  1302.     if (!s)
  1303.         break;
  1304.     
  1305.     search_status = (*search) (view, text, s + 1, match_normal);
  1306.     if (search_status < 0)
  1307.         break;
  1308.  
  1309.     if (search_status == 0)
  1310.         continue;
  1311.  
  1312.     /* We found the string */
  1313.     
  1314.     if (!isatbeg && !view->search_start){
  1315.         
  1316.         /* We do not want to match a
  1317.          * ^ regexp when not at the real
  1318.          * beginning of some line
  1319.          */
  1320.         view->found_len = found_len;
  1321.         view->search_start = search_start;
  1322.         if ((*search) (view, text, s, match_normal) <= 0)
  1323.         continue;
  1324.         (*search) (view, text, s + 1, match_normal);
  1325.     }
  1326.     /* Record the position used to continue the search */
  1327.     if (view->direction == 1)
  1328.         t = forward_line_start;
  1329.     else
  1330.         t = reverse_line_start ? reverse_line_start + 3 : 0;
  1331.     view->search_start += t;
  1332.  
  1333.     if (t != beginning){
  1334.         if (t > get_bottom_first (view, 0, 0))
  1335.         view->start_display = view->bottom_first;
  1336.         else
  1337.         view->start_display = t;
  1338.     }
  1339.     
  1340.     free (s);
  1341.     break;
  1342.     }
  1343.     disable_interrupt_key ();
  1344.     if (verbose){
  1345.     dlg_run_done (d);
  1346.     destroy_dlg (d);
  1347.     }
  1348.  
  1349.     if (!s){
  1350.     message (0, " Search ", " Search string not found ");
  1351.     view->found_len = 0;
  1352.     }
  1353. }
  1354.  
  1355. /* Search buffer (it's size is len) in the complete buffer */
  1356. /* returns the position where the block was found */
  1357. static long block_search (WView *view, char *buffer, int len)
  1358. {
  1359.     int w = view->widget.cols - (view->have_frame * 2);
  1360.     char *d = buffer;
  1361.     long e;
  1362.  
  1363.     /* clear interrupt status */
  1364.     got_interrupt ();
  1365.     e = view->found_len ? view->search_start + 1 : view->search_start;
  1366.  
  1367.     search_update_steps (view);
  1368.     update_activate = 0;
  1369.     
  1370.     for (; e < view->last_byte; e++){
  1371.     if (e >= update_activate){
  1372.         update_activate += update_steps;
  1373.         if (verbose){
  1374.         view_percent (view, e, w);
  1375.         refresh ();
  1376.         }
  1377.         if (got_interrupt ())
  1378.         break;
  1379.     }
  1380.     disable_interrupt_key ();
  1381.     if (*d == get_byte (view, e))
  1382.         d++;
  1383.     else {
  1384.         e -= d - buffer;
  1385.         d = buffer;
  1386.     }
  1387.     enable_interrupt_key ();
  1388.     if (d - buffer == len){
  1389.         disable_interrupt_key ();
  1390.         return e - len;
  1391.     }
  1392.     }
  1393.     return 0;
  1394. }
  1395.  
  1396. /* States of our funny recognizer */
  1397. enum {normal, inside_quotes, zero, hex1, hex2, oct1};
  1398.  
  1399. /* This routine doesn't report all the user mistakes, it just ignores them */
  1400. static void hex_search (WView *view, char *text)
  1401. {
  1402.     char buffer [120];        /* Where we hold the information */
  1403.     int  i, block_len;
  1404.     int  v = 0;
  1405.     long pos;            /* Where did we found the string */
  1406.     char *p;            /* Temporary */
  1407.     int  state = normal;    /* Initial state of the micro-scanner */
  1408.     
  1409.     /* First convert the string to a stream of bytes */
  1410.     for (i = block_len = 0; text [i] && block_len < sizeof (buffer); i++){
  1411.     switch (state){
  1412.     case inside_quotes:
  1413.         if (text [i] == '"')
  1414.         state = normal;
  1415.         else
  1416.         buffer [block_len++] = text [i];
  1417.         break;
  1418.  
  1419.     case normal:
  1420.         if (text [i] == '"'){
  1421.         state = inside_quotes;
  1422.         break;
  1423.         }
  1424.         if (text [i] == '0'){
  1425.         state = zero;
  1426.         break;
  1427.         }
  1428.         if (text [i] == 'x'){
  1429.         state = hex1;
  1430.         break;
  1431.         }
  1432.         break;
  1433.  
  1434.     case zero:
  1435.         if (text [i] == 'x')
  1436.         state = hex1;
  1437.         break;
  1438.  
  1439.     case hex1:
  1440.         v = 0;
  1441.         text [i] = toupper (text [i]);
  1442.         if ((p = strchr (hex_char, text [i])) != 0){
  1443.         v = (p - hex_char) << 4;
  1444.         state = hex2;
  1445.         }
  1446.         break;
  1447.  
  1448.     case hex2:
  1449.         if ((p = strchr (hex_char, text [i])) != 0){
  1450.         v |= (p - hex_char);
  1451.         state = normal;
  1452.         }
  1453.         buffer [block_len++] = v;
  1454.         break;
  1455.     }
  1456.     }
  1457.     /* Then start the search */
  1458.     pos = block_search (view, buffer, block_len);
  1459.     if (!pos){
  1460.     message (0, " Search ", " Search string not found ");
  1461.     view->found_len = 0;
  1462.     return;
  1463.     }
  1464.     
  1465.     view->search_start = pos + 1;
  1466.     view->found_len = block_len;
  1467.     
  1468.     /* Adjust the file offset */
  1469.     view->start_display = (pos & (~(view->bytes_per_line-1)));
  1470.     if (view->start_display > get_bottom_first (view, 0, 0))
  1471.         view->start_display = view->bottom_first;
  1472. }
  1473.  
  1474. static int regexp_view_search (WView *view, char *pattern, char *string, int match_type)
  1475. {
  1476.     static regex_t r;
  1477.     static char *old_pattern = NULL;
  1478.     static int old_type;
  1479.     int startpos, found_len, len;
  1480.  
  1481.     if (!old_pattern || strcmp (old_pattern, pattern) || old_type != match_type){
  1482.     if (old_pattern){
  1483.         regfree (&r);
  1484.         free (old_pattern);
  1485.     }
  1486.     if (regcomp (&r, pattern, REG_EXTENDED|REG_NOSUB)){
  1487.         message (1, " Error ", " Invalid regular expression ");
  1488.         return -1;
  1489.     }
  1490.     old_pattern = strdup (pattern);
  1491.     old_type = match_type;
  1492.     }
  1493.     len = strlen (string);
  1494.     startpos = re_search (&r, string, len, 0, len, NULL);
  1495.     if (startpos < 0)
  1496.         return 0;
  1497.     found_len = re_match (&r, string, len, startpos, NULL);
  1498.     view->found_len = found_len;
  1499.     view->search_start = startpos;
  1500.     return 1;
  1501. }
  1502.  
  1503. static void do_regexp_search (void *xview, char *regexp)
  1504. {
  1505.     WView *view = (WView *) xview;
  1506.     
  1507.     view->search_exp = regexp;
  1508.     search (view, regexp, regexp_view_search);
  1509.     /* Had a refresh here */
  1510.     view->dirty++;
  1511.     view_update (view);
  1512. }
  1513.  
  1514. static void do_normal_search (void *xview, char *text)
  1515. {
  1516.     WView *view = (WView *) xview;
  1517.     
  1518.     view->search_exp = text;
  1519.     if (view->hex_mode)
  1520.     hex_search (view, text);
  1521.     else 
  1522.     search (view, text, icase_search_p);
  1523.     /* Had a refresh here */
  1524.     view->dirty++;
  1525.     view_update (view);
  1526. }
  1527.  
  1528. /* }}} */
  1529. /* {{{ Mouse and keyboard handling */
  1530.  
  1531. /* Real view only */
  1532. static void help_cmd (void)
  1533. {
  1534.     interactive_display (LIBDIR "mc.hlp", "[Internal File Viewer]");
  1535.     /*
  1536.     view_refresh (0);
  1537.     */
  1538. }
  1539.  
  1540. /* Both views */
  1541. void toggle_wrap_mode (WView *view)
  1542. {
  1543.     if (view->hex_mode){
  1544.         get_bottom_first (view, 1, 0);
  1545.     return;
  1546.     }
  1547.     wrap_mode = 1 - wrap_mode;
  1548.     get_bottom_first (view, 1, 0);
  1549.     if (wrap_mode)
  1550.     view->start_col = 0;
  1551.     else {
  1552.     if (have_fast_cpu){
  1553.         if (view->bottom_first < view->start_display)
  1554.         view->search_start = view->start_display = view->bottom_first;
  1555.             view->found_len = 0;
  1556.     }
  1557.     }
  1558.     view_labels (view);
  1559.     view->dirty++;
  1560.     view_update (view);
  1561. }
  1562.  
  1563. /* Both views */
  1564. void toggle_hex_mode (WView *view)
  1565. {
  1566.     view->hex_mode = 1 - view->hex_mode;
  1567.     altered_hex_mode = 1;
  1568.     get_bottom_first (view, 1, 0);
  1569.     view_labels (view);
  1570.     view->dirty++;
  1571.     view_update (view);
  1572. }
  1573.  
  1574. /* Both views */
  1575. void goto_line (WView *view)
  1576. {
  1577.     char *line, prompt [100];
  1578.     int i, oldline = 1;
  1579.  
  1580.     for (i = view->first; i < view->start_display; i++)
  1581.     if (get_byte (view, i) == '\n')
  1582.         oldline ++;
  1583.     sprintf (prompt, " The current line number is %d.\n"
  1584.                  " Enter the new line number:", oldline);
  1585.     line = input_dialog (" Goto line ", prompt, "");
  1586.     if (line){
  1587.     if (*line){
  1588.         move_to_top (view);
  1589.         move_forward (view, atoi (line) - 1);
  1590.     }
  1591.     free (line);
  1592.     }
  1593.     view->dirty++;
  1594.     view_update (view);
  1595. }
  1596.  
  1597. /* Both views */
  1598. void regexp_search (WView *view, int direction)
  1599. {
  1600.     char *regexp = "";
  1601.     static char *old = 0;
  1602.  
  1603.     if (view->hex_mode)
  1604.     return;
  1605.     
  1606.     regexp = old ? old : regexp;
  1607.     regexp = input_dialog (" Search ", " Enter regexp:", regexp);
  1608.     if ((!regexp) || (!*regexp)){
  1609.     regexp = "";
  1610.     return;
  1611.     }
  1612.     if (old)
  1613.     free (old);
  1614.     old = strdup (regexp);
  1615. #if 0
  1616.     /* Mhm, do we really need to load all the file in the core? */
  1617.     if (view->bytes_read < view->last_byte)
  1618.     get_byte (view, view->last_byte-1);/* Get the whole file in to memory */
  1619. #endif
  1620.     view->direction = direction;
  1621.     do_regexp_search (view, regexp);
  1622.  
  1623.     view->last_search = do_regexp_search;
  1624. }
  1625.  
  1626. void regexp_search_cmd (WView *view)
  1627. {
  1628.     regexp_search (view, 1);
  1629. }
  1630.  
  1631. /* Both views */
  1632. void normal_search (WView *view, int direction)
  1633. {
  1634.     static char *old;
  1635.     char *exp = "";
  1636.  
  1637.     exp = old ? old : exp;
  1638.     exp = input_dialog (" Search ", " Enter search string:", exp);
  1639.     if ((!exp) || (!*exp)){
  1640.     exp = "";
  1641.     return;
  1642.     }
  1643.     if (old)
  1644.     free (old);
  1645.     old = strdup (exp);
  1646. /* FIXME: add this after 2.0: free (exp) */
  1647.  
  1648.     view->direction = direction;
  1649.     do_normal_search (view, exp);
  1650.     view->last_search = do_normal_search;
  1651. }
  1652.  
  1653. void normal_search_cmd (WView *view)
  1654. {
  1655.     normal_search (view, 1);
  1656. }
  1657.  
  1658. void change_viewer (WView *view)
  1659. {
  1660.     altered_magic_flag = 1;
  1661.     if (!view->command){
  1662.         view->viewer_magic_flag = !view->viewer_magic_flag;
  1663.         view_labels (view);
  1664.     } else {
  1665.         char *s = strdup (view->filename);
  1666.         char *t = strdup (view->command);
  1667.  
  1668.         view_done (view);
  1669.  
  1670.     /* Is it possible to not use the growing buffers? */
  1671.     if (!(*t && !*s))
  1672.         view->viewer_magic_flag = !view->viewer_magic_flag;
  1673.         view_init (view, t, s);
  1674.         free (s);
  1675.         free (t);
  1676.         view_labels (view);
  1677.         view->dirty++;
  1678.         view_update (view);
  1679.     }
  1680. }
  1681.  
  1682. static void change_nroff (WView *view)
  1683. {
  1684.     view->viewer_nroff_flag = !view->viewer_nroff_flag;
  1685.     altered_nroff_flag = 1;
  1686.     view_labels (view);
  1687.     view->dirty++;
  1688.     view_update (view);
  1689. }
  1690.  
  1691. /* Real view only */
  1692. static void quit_cmd (WView *view)
  1693. {
  1694.     view->widget.parent->running = 0;
  1695. }
  1696.  
  1697. /* Both views */
  1698. void view_labels (WView *view)
  1699. {
  1700.     Dlg_head *h = view->widget.parent;
  1701.     
  1702.     define_label (h, (Widget *) view, 1, "Help", help_cmd);
  1703.     
  1704.     my_define (h, 10, "Quit", quit_cmd, view);
  1705.     my_define (h, 4, view->hex_mode ?"Ascii":"Hex", toggle_hex_mode, view);
  1706.     my_define (h, 5, "Line", goto_line, view);
  1707.     my_define (h, 6, view->hex_mode ? "" : "RxSrch", regexp_search_cmd, view);
  1708.  
  1709.     my_define (h, 2, view->hex_mode ? "" : wrap_mode ? "UnWrap" : "Wrap",
  1710.            toggle_wrap_mode, view);
  1711.     
  1712.     my_define (h, 7, view->hex_mode ? "HxSrch" : "Search",
  1713.            normal_search_cmd, view);
  1714.     
  1715.     my_define (h, 8, view->viewer_magic_flag ? "Raw" : "Parse",
  1716.            change_viewer, view);
  1717.  
  1718.     if (!view->have_frame){
  1719.     my_define (h, 9, view->viewer_nroff_flag ? "Unform" : "Format",
  1720.            change_nroff, view);
  1721.     my_define (h, 3, "Quit", quit_cmd, view);
  1722.     }
  1723.     
  1724.     redraw_labels (h, (Widget *) view);
  1725. }
  1726.  
  1727. /* Both views */
  1728. static int check_left_right_keys (WView *view, int c)
  1729. {
  1730.     if (c == KEY_LEFT)
  1731.     move_left (view);
  1732.     else if (c == KEY_RIGHT)
  1733.     move_right (view);
  1734.     else return 0;
  1735.  
  1736.     return 1;
  1737. }
  1738.  
  1739. enum { off, on };
  1740.  
  1741. static void set_monitor (WView *view, int set_on)
  1742. {
  1743.     int old = view->monitor;
  1744.     
  1745.     view->monitor = set_on;
  1746.     
  1747.     if (view->monitor){
  1748.     move_to_bottom (view);
  1749.     view->bottom_first = -1;
  1750.     set_idle_proc (view->widget.parent, 1);
  1751.     } else {
  1752.     if (old)
  1753.         set_idle_proc (view->widget.parent, 0);
  1754.     }
  1755. }
  1756.  
  1757. /* Both views */
  1758. static int view_handle_key (WView *view, int c)
  1759. {
  1760.     int prev_monitor = view->monitor;
  1761.  
  1762.     set_monitor (view, off);
  1763.     
  1764.     if (check_left_right_keys (view, c))
  1765.     return 1;
  1766.     
  1767.     if (check_movement_keys (c, 1, vheight, view, (movefn) move_backward, (movefn) move_forward, (movefn) move_to_top, (movefn) move_to_bottom)){
  1768.     return 1;
  1769.     }
  1770.     
  1771.     switch (c){
  1772.  
  1773.     case '?':
  1774.     regexp_search (view, -1);
  1775.     return 1;
  1776.     
  1777.     case '/':
  1778.     regexp_search (view, 1);
  1779.     return 1;
  1780.  
  1781.     /* Continue search */
  1782.     case XCTRL('s'):
  1783.     case 'n':
  1784.     if (view->last_search){
  1785.         (*view->last_search)(view, view->search_exp);
  1786.     } else {
  1787.         /* if not... then ask for an expression */
  1788.         normal_search (view, 1);
  1789.     }
  1790.     return 1;
  1791.  
  1792.     case XCTRL('r'):
  1793.     if (view->last_search){
  1794.         (*view->last_search)(view, view->search_exp);
  1795.     } else {
  1796.         normal_search (view, -1);
  1797.     }
  1798.     return 1;
  1799.     
  1800.     case 'h':
  1801.         move_left (view);
  1802.         return 1;
  1803.         
  1804.     case 'j':
  1805.     case '\n':
  1806.     case 'e':
  1807.         move_forward (view, 1);
  1808.         return 1;
  1809.         
  1810.     case 'd':
  1811.         move_forward (view, vheight / 2);
  1812.         return 1;
  1813.         
  1814.     case 'u':
  1815.         move_backward (view, vheight / 2);
  1816.         return 1;
  1817.         
  1818.     case 'k':
  1819.     case 'y':
  1820.         move_backward (view, 1);
  1821.         return 1;
  1822.         
  1823.     case 'l':
  1824.         move_right (view);
  1825.         return 1;
  1826.         
  1827.     case ' ':
  1828.     case 'f':
  1829.         move_forward (view, vheight - 1);
  1830.         return 1;
  1831.  
  1832.     case '!':
  1833.     exec_shell ();
  1834.     return 1;
  1835.     
  1836.     case 'F':
  1837.     set_monitor (view, on);
  1838.     return 1;
  1839.     
  1840.     case 'b':
  1841.         move_backward (view, vheight - 1);
  1842.         return 1;
  1843.         
  1844.     case KEY_IC:
  1845.         move_backward (view, 2);
  1846.         return 1;
  1847.         
  1848.     case KEY_DC:
  1849.         move_forward (view, 2);
  1850.         return 1;
  1851.  
  1852.     case 'm':
  1853.     view->marks [view->marker] = view->start_display;
  1854.     return 1;
  1855.  
  1856.     case 'r':
  1857.     view->start_display = view->marks [view->marker];
  1858.     view->dirty++;
  1859.     return 1;
  1860.     
  1861.     /*  Use to indicate parent that we want to see the next/previous file */
  1862.     /* Only works on full screen mode */
  1863.     case XCTRL('f'):
  1864.     case XCTRL('b'):
  1865.     if (!view->have_frame)
  1866.         view->move_dir = c == XCTRL('f') ? 1 : -1;
  1867.     /* fall */
  1868.  
  1869.     case 'q':
  1870.     case XCTRL('g'):
  1871.     case ESC_CHAR:
  1872.     view->view_quit = 1;
  1873.     return 1;
  1874.  
  1875.     }
  1876.     if (c >= '0' && c <= '9')
  1877.     view->marker = c - '0';
  1878.  
  1879.     /* Restore the monitor status */
  1880.     set_monitor (view, prev_monitor);
  1881.     
  1882.     /* Key not used */
  1883.     return 0;
  1884. }
  1885.  
  1886. /* Both views */
  1887. int view_event (WView *view, Gpm_Event *event, int *result)
  1888. {
  1889.     *result = MOU_NORMAL;
  1890.     if (event->type & (GPM_DOWN|GPM_DRAG)){
  1891.         if (!wrap_mode){
  1892.             if (event->x < view->widget.cols / 4){
  1893.                 move_left (view);
  1894.                 *result = MOU_REPEAT;
  1895.                 return 1;
  1896.             }
  1897.             if (event->x > 3 * vwidth / 4){
  1898.                 move_right (view);
  1899.                 *result = MOU_REPEAT;
  1900.                 return 1;
  1901.             }
  1902.     }
  1903.     if (event->y < view->widget.lines / 3){
  1904.         if (mouse_move_pages_viewer)
  1905.             move_backward (view, view->widget.lines / 2 - 1);
  1906.         else
  1907.             move_backward (view, 1);
  1908.         *result = MOU_REPEAT;
  1909.         return 1;
  1910.     }
  1911.     else if (event->y > 2 * vheight /3){
  1912.         if (mouse_move_pages_viewer)
  1913.             move_forward (view, vheight / 2 - 1);
  1914.         else
  1915.             move_forward (view, 1);
  1916.         *result = MOU_REPEAT; 
  1917.         return 1;
  1918.     }
  1919.     }
  1920.     return 0;
  1921. }
  1922.  
  1923. /* Real view only */
  1924. int real_view_event (Gpm_Event *event, void *x)
  1925. {
  1926.     int result;
  1927.     
  1928.     if (view_event ((WView *) x, event, &result))
  1929.         view_update ((WView *) x);
  1930.     return result;
  1931. }
  1932.  
  1933. /* }}} */
  1934. /* {{{ Window creation, destruction and a driver stub for real view */
  1935.  
  1936. static int view_mode_callback (struct Dlg_head *h, int id, int msg)
  1937. {
  1938.     return default_dlg_callback (h, id, msg);
  1939. }
  1940.  
  1941. #ifdef HAVE_XVIEW
  1942. /* Real view only */
  1943.  
  1944. void view_adjust_size (Dlg_head *unused)
  1945. {
  1946. }
  1947.  
  1948. int view (char *_command, char *_file, int *move_dir_p)
  1949. {
  1950.     int midnight_colors [4];
  1951.     int error;
  1952.     WView      *wview;
  1953.  
  1954.     wview = view_new (0, 0, COLS, LINES - 1, 0);
  1955.  
  1956.     error = view_init (wview, _command, _file);
  1957.     if (!error){
  1958.     x_view (wview);    
  1959.     }
  1960.     *move_dir_p = 0;
  1961.     return !error;
  1962. }
  1963. #else
  1964. Dlg_head   *view_dlg;
  1965.  
  1966. void view_adjust_size (Dlg_head *h)
  1967. {
  1968.     WView      *view;
  1969.     WButtonBar *bar;
  1970.  
  1971.     /* Look up the viewer and the buttonbar, we assume only two widgets here */
  1972.     view = (WView *) find_widget_type (h, (void *) &view_callback);
  1973.     bar  = view->widget.parent->current->next->widget;
  1974.     widget_set_size (&view->widget, 0, 0, LINES-1, COLS);
  1975.     widget_set_size (&bar->widget, LINES-1, 0, 1, COLS);
  1976. }
  1977.  
  1978. /* Real view only */
  1979. int view (char *_command, char *_file, int *move_dir_p)
  1980. {
  1981.     int midnight_colors [4];
  1982.     int error;
  1983.     WView *wview;
  1984.     WButtonBar *bar;
  1985.  
  1986.     
  1987.     /* Create dialog and widgets, put them on the dialog */
  1988.     view_dlg = create_dlg (0, 0, LINES, COLS, midnight_colors,
  1989.                view_mode_callback, "[Internal File Viewer]",
  1990.                "view",
  1991.                DLG_NONE);
  1992.  
  1993.     wview = view_new (0, 0, COLS, LINES-1, 0);
  1994.  
  1995.     bar  = buttonbar_new (1);
  1996.  
  1997.     add_widget (view_dlg, wview);
  1998.     add_widget (view_dlg, bar);
  1999.  
  2000.     error = view_init (wview, _command, _file);
  2001.     if (move_dir_p)
  2002.     *move_dir_p = 0;
  2003.  
  2004.     /* Please note that if you add another widget,
  2005.      * you have to modify view_adjust_size to
  2006.      * be aware of it
  2007.      */
  2008.     if (!error){
  2009.     run_dlg (view_dlg);
  2010.     if (move_dir_p)
  2011.         *move_dir_p = wview->move_dir;
  2012.     }
  2013.     destroy_dlg (view_dlg);
  2014.     
  2015.     return !error;
  2016. }
  2017. #endif
  2018.  
  2019. static void view_hook (void *v)
  2020. {
  2021.     WView *view = (WView *) v;
  2022.     WPanel *panel;
  2023.  
  2024.     /* If the user is busy typing, wait until he finishes to update the
  2025.        screen */
  2026.     if (!is_idle ()){
  2027.     if (!hook_present (idle_hook, view_hook))
  2028.         add_hook (&idle_hook, view_hook, v);
  2029.     return;
  2030.     }
  2031.  
  2032.     delete_hook (&idle_hook, view_hook);
  2033.     
  2034.     if (get_current_type () == view_listing)
  2035.     panel = current_panel;
  2036.     else if (get_other_type () == view_listing)
  2037.     panel = other_panel;
  2038.     else
  2039.     return;
  2040.     
  2041.     if (!S_ISREG (panel->dir.list [panel->selected].buf.st_mode))
  2042.     return;
  2043.     
  2044.     view_init (view, 0, panel->dir.list [panel->selected].fname);
  2045.     display (view);
  2046.     view_status (view);
  2047. }
  2048.  
  2049. static int view_callback (Dlg_head *h, WView *view, int msg, int par)
  2050. {
  2051.     int v;
  2052.     
  2053.     switch (msg){
  2054.     case WIDGET_INIT:
  2055.     x_create_viewer (view);
  2056.     if (view->have_frame)
  2057.         add_hook (&select_file_hook, view_hook, view);
  2058.     else
  2059.         view_labels (view);
  2060.     break;
  2061.     
  2062.     case WIDGET_DRAW:
  2063.     display (view);
  2064.     view_status (view);
  2065.     break;
  2066.  
  2067.     case WIDGET_KEY:
  2068.     v = view_handle_key ((WView *)view, par);
  2069.     if (view->view_quit)
  2070.         h->running = 0;
  2071.     else {
  2072.         view_update (view);
  2073.     }
  2074.     return v;
  2075.  
  2076.     case WIDGET_IDLE:
  2077.     /* This event is generated when the user is using the 'F' flag */
  2078.     view->bottom_first = -1;
  2079.     move_to_bottom (view);
  2080.     display (view);
  2081.     view_status (view);
  2082.     sleep (1);
  2083.     return 1;
  2084.     
  2085.     case WIDGET_FOCUS:
  2086. #ifdef HAVE_TK
  2087.     tk_evalf ("focus %s.v.view", wtk_win (view->widget));
  2088. #endif
  2089.     view_labels (view);
  2090.     return 1;
  2091.     
  2092.     }
  2093.     return default_proc (h, msg, par);
  2094. }
  2095.  
  2096. WView *view_new (int y, int x, int cols, int lines, int is_panel)
  2097. {
  2098.     WView *view = xmalloc (sizeof (WView), "view_new");
  2099.     
  2100.     init_widget (&view->widget, y, x, lines, cols,
  2101.          (callback_fn) view_callback,
  2102.          (destroy_fn) view_destroy,
  2103.          (mouse_h) real_view_event);
  2104.  
  2105.     view->filename = 0;
  2106.     view->view_active = 0;
  2107.     view->bottom_first = 0;
  2108.     view->start_col = 0;
  2109.     view->dirty = 0;
  2110.     view->hex_mode = default_hex_mode;
  2111.     view->viewer_magic_flag = default_magic_flag;
  2112.     view->viewer_nroff_flag = default_nroff_flag;
  2113.     view->view_quit = 0;
  2114.     view->move_dir = 0;
  2115.     view->have_frame = is_panel;
  2116.     view->last_byte = -1;
  2117.     view->monitor = 0;
  2118. #ifdef HAVE_TK
  2119.     view->status_shown = 0;
  2120.     view->current_line = 1;
  2121.     view->cache_len = 80;
  2122.     view->last_col = 0;
  2123.     view->cache = xmalloc (81, "view->cache");
  2124.     view->color_cache = xmalloc (81, "view->cache");
  2125.     view->direction = 1;
  2126.     bzero (view->cache, 81);
  2127.     view->dest = 0;
  2128. #endif
  2129.     widget_want_cursor (view->widget, 0);
  2130.  
  2131.     return view;
  2132. }
  2133.  
  2134. /* }}} */
  2135. /* {{{ Emacs local variables */
  2136.  
  2137. /*
  2138.    Cause emacs to enter folding mode for this file:
  2139.    Local variables:
  2140.    end:
  2141.    */
  2142.  
  2143. /* }}} */
  2144.